package org.aalku.adbcopy;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

public class Engine {
	private static final String TAB = "  ";
	String adbCommand = System.getProperty("ADB_COMMAND",
			"windows/win32/adb.exe");

	private static class RemoteLsEntry {

		private String name;
		private boolean directory;
		private boolean link;

		public RemoteLsEntry(String lsLine) {
			String[] split = lsLine.split("\\s+", 2);
			this.name = split[1];
			this.directory = split[0].indexOf('d') >= 0;
			this.link = split[0].indexOf('l') >= 0;
		}

		@Override
		public String toString() {
			return (directory ? "d" : "f") + (link ? "l" : " ") + " " + name;
		}

	}

	public void recursiveRetrive(String source, String dest,
			Pattern filePattern, boolean removeEmpty) throws IOException, InterruptedException {
		printRoot(source);
		recursiveRetrive(source, dest, filePattern, removeEmpty, 0);
	}
	
	private void recursiveRetrive(String source, String dest,
			Pattern filePattern, boolean removeEmpty, int depth)
			throws IOException, InterruptedException {
		StringBuilder tab = new StringBuilder();
		for (int i = 0; i <= depth; i++) {
			tab.append(TAB);
		}
		File dir = new File(dest);
		dir.mkdirs();
		List<? extends RemoteLsEntry> files = remoteLs(source);
		for (RemoteLsEntry f : files) {
			if (!f.directory && !f.link) {
				if (filePattern == null
						|| filePattern.matcher(f.name).matches()) {
					printFile(tab, f);
					@SuppressWarnings("unused")
					List<String> out = adb("pull", source + "/" + f.name, dest
							+ "/" + f.name);
					// System.out.println(out);
				}
			}
		}
		for (RemoteLsEntry f : files) {
			if (f.directory && !f.link) {
				printDir(tab, f);
				recursiveRetrive(source + "/" + f.name, dest + "/" + f.name,
						filePattern, removeEmpty, depth + 1);
			}
		}
		if (removeEmpty && dir.list().length == 0) {
			dir.delete();
		}
	}
	
	protected void printDir(StringBuilder tab, RemoteLsEntry f) {
		System.out.println(tab + "|" + f.name + "/");
	}
	
	protected void printFile(StringBuilder tab, RemoteLsEntry f) {
		System.out.println(tab + "|-" + f.name);
	}
	
	protected void printRoot(String source) {
		System.out.println(source + "/");
	}

	protected List<? extends RemoteLsEntry> remoteLs(String path) throws IOException,
			InterruptedException {
		List<String> out = adb("shell",
				"ls -Fa \"" + path.replace("\"", "\\\"") + "\"");
		String first = out.isEmpty() ? "" : out.get(0);
		String firstPart = first.split(" ", 2)[0];
		if (firstPart.endsWith(":")
				|| first.equals("opendir failed, Permission denied")
				|| firstPart.length() > 3) {
			System.out.println("Error: " + out);
			return Collections.emptyList();
		}
		List<RemoteLsEntry> res = new ArrayList<RemoteLsEntry>();
		for (String l : out) {
			if (!l.isEmpty()) {
				res.add(new RemoteLsEntry(l));
			}
		}
		return res;
	}
	
	protected List<String> mounts() throws IOException, InterruptedException {
		List<String> out = adb("shell", "mount");
		return out;
	}

	public List<String> getDevices() throws IOException, InterruptedException {
		List<String> devices = new ArrayList<String>();
		List<String> output = adb("devices");
		Iterator<String> outIt = output.iterator();
		if (!outIt.hasNext()
				|| !outIt.next().trim()
						.equalsIgnoreCase("List of devices attached")) {
			throw new RuntimeException("Unexpected adb output");
		}
		while (outIt.hasNext()) {
			String s = outIt.next().trim();
			if (!s.isEmpty()) {
				devices.add(s);
			}
		}
		return devices;
	}

	protected List<String> adb(String... args) throws IOException,
			InterruptedException {
		// System.err.println("# " + adbCommand + " " + Arrays.toString(args));
		List<String> lArgs = new LinkedList<String>(Arrays.asList(args));
		lArgs.add(0, adbCommand);
		ProcessBuilder processBuilder = new ProcessBuilder(lArgs);
		processBuilder.redirectErrorStream(false);
		Process process = processBuilder.start();
		final InputStream in = process.getInputStream();
		StreamReaderThread thread = new StreamReaderThread(in);
		thread.start();
		while (thread.isAlive()) {
			thread.join(20000);
			if (thread.isAlive() && !thread.anyOutput()) {
				process.destroy();
			}
		}
		List<String> output = thread.getOutputLines();
		int res = process.waitFor();
		if (res != 0) {
			return Collections.emptyList();
		} else {
			return output;
		}
	}

	public Collection<String> getStorageDevices() throws IOException, InterruptedException {
		String root = "/storage/";
		List<String> res = new ArrayList<String>();
		List<? extends RemoteLsEntry> roots = remoteLs(root);
		for (RemoteLsEntry f: roots) {
			if (f.directory) {
				String root2 = root + f.name;
				List<? extends RemoteLsEntry> roots2 = remoteLs(root2);
				if (!roots2.isEmpty()) {
					res.add(root2);
				}
			}
		}
		return res;
	}
}
